package ext;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Stream;

public class FilesSample {

    public static void main(String[] args) throws Throwable {
        // =================================
        // 1. exists 测试路径(目录或文件)是否存在
        // =================================
        boolean exists;
        // ===> true
        exists = Files.exists(Paths.get("C:\\Windows\\System32"));
        // ===> true
        exists = Files.exists(Paths.get("C:\\Windows\\System32", "cmd.exe"));
        // ===> false
        exists = Files.exists(Paths.get("C:\\Windows\\System32", "cmd2.exe"));
        // =================================
        // 2. 创建目录, 目录如果已经存在会报FileAlreadyExistsException
        // =================================
        Path directory;
        // ===> C:\AppData\MyDir
        directory = Files.createDirectory(Paths.get("C:\\AppData\\MyDir"));
        // ===> C:\AppData\MyDir\2022-11-22\Files
        directory = Files.createDirectories(Paths.get("C:\\AppData\\MyDir\\2022-11-22\\Files"));
        // ===> C:\Users\FANLYC~1\AppData\Local\Temp\MyTemp936057541651649770
        directory = Files.createTempDirectory("MyTemp");
        // ===> C:\AppData\MyDir\MyTemp15794822332079051300
        directory = Files.createTempDirectory(Paths.get("C:\\AppData\\MyDir"), "MyTemp");
        // =================================
        // 3. 创建文件, 文件如果已经存在会报FileAlreadyExistsException
        // =================================
        Path filePath;
        // ===> C:\AppData\MyDir\newFile.txt
        filePath = Files.createFile(Paths.get("C:\\AppData\\MyDir", "newFile.txt"));
        // ===> C:\Users\FANLYC~1\AppData\Local\Temp\newFile4801872645777588402.txt
        filePath = Files.createTempFile("newFile", ".txt");
        // ===> C:\AppData\MyDir\newFile15849339911481841613.txt
        filePath = Files.createTempFile(Paths.get("C:\\AppData\\MyDir"), "newFile", ".txt");
        // =================================
        // 4. 写文件
        // =================================
        // =================================
        // StandardOpenOption:
        // READ: 读访问
        // WRITE: 在文件的头部写(新数据会覆盖原文件内容), 如果文件不存在报NoSuchFileException
        // APPEND: 追加到文件, 在文件的尾部写(不会覆盖原文件内容), 如果文件不存在报NoSuchFileException
        // TRUNCATE_EXISTING: 先清空文件数据, 如果文件不存在报NoSuchFileException
        // CREATE: 如果文件不存在则创建文件, 如果文件存在则打开文件(新数据会覆盖原文件内容)
        // CREATE_NEW: 创建文件, 如果文件已经存在则报FileAlreadyExistsException
        // DELETE_ON_CLOSE: 当文件流关闭后删除文件
        // SPARSE:
        // SYNC:
        // DSYNC:
        // =================================
        // 将列表集合的数据写到文件
        Files.write(
                Paths.get("C:\\AppData", "myFile.txt"),
                List.of(
                        "This is the 1 line",
                        "This is the 2 line",
                        "This is the 3 line"
                ),
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND
        );
        // 将列表集合的数据写到文件, 指定字符编码
        Files.write(
                Paths.get("C:\\AppData", "myFile.txt"),
                List.of(
                        "This is the 4 line",
                        "This is the 5 line"
                ),
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND
        );
        // 将一个字符串写到文件(不会自动换行)
        Files.writeString(
                Paths.get("C:\\AppData", "myFile.txt"),
                "This is the 6 line",
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND
        );
        // 将一个字符串写到文件, 指定字符编码(不会自动换行)
        Files.writeString(
                Paths.get("C:\\AppData", "myFile.txt"),
                "This is the 7 line",
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND
        );
        // =================================
        // 5. 读文件
        // =================================
        List<String> lines;
        String texts;
        lines = Files.readAllLines(Paths.get("C:\\AppData", "myFile.txt"));
        lines = Files.readAllLines(Paths.get("C:\\AppData", "myFile.txt"), StandardCharsets.UTF_8);
        texts = Files.readString(Paths.get("C:\\AppData", "myFile.txt"));
        texts = Files.readString(Paths.get("C:\\AppData", "myFile.txt"), StandardCharsets.UTF_8);
        // =================================
        // 6. 删除文件或目录
        // =================================
        // delete:
        // 1. 文件或目录不存在报NoSuchFileException
        // 2. 只能删除空目录, 若删除目录时目录不为空报DirectoryNotEmptyException
        Files.delete(Paths.get("C:\\AppData", "myFile.txt"));
        // deleteIfExists
        // 1. 文件或目录不存在不执行删除动作, 不会报异常
        // 2. 只能删除空目录, 若删除目录时目录不为空报DirectoryNotEmptyException
        Files.deleteIfExists(Paths.get("C:\\AppData", "myFile.txt"));
        // =================================
        // 7. 复制文件
        // =================================
        Files.copy(
                Paths.get("C:\\AppData", "newFile.txt"),
                new FileOutputStream("C:\\AppData\\copyFile.txt")
        );
        Files.copy(
                Paths.get("C:\\AppData", "newFile.txt"),
                Paths.get("C:\\AppData", "copyFile.txt"),
                StandardCopyOption.REPLACE_EXISTING
        );
        Files.copy(
                new FileInputStream("C:\\AppData\\newFile.txt"),
                Paths.get("C:\\AppData", "copyFile.txt"),
                StandardCopyOption.REPLACE_EXISTING
        );
        // =================================
        // 8. 遍历迭代
        // =================================
        // 遍历目录
        try (Stream<Path> pathStream = Files.walk(Paths.get("C:\\AppData\\MyDir"))) {
            pathStream.filter(Files::isDirectory)
                    .forEach(System.out::println);
        }
        // 遍历文件
        try (Stream<Path> pathStream = Files.walk(Paths.get("C:\\AppData\\MyDir"))) {
            pathStream.filter(Files::isRegularFile)
                    .forEach(System.out::println);
        }
        // 遍历目录或文件
        // 1. 进入目录执行preVisitDirectory
        // 2. 目录有文件则访问文件执行visitFile
        // 3. 目录下文件遍历完后执行postVisitDirectory并返回上一级路径
        Files.walkFileTree(Paths.get("C:\\AppData\\MyDir"), new SimpleFileVisitor<>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                System.out.println("preVisitDirectory: " + dir);
                return super.preVisitDirectory(dir, attrs);
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println("visitFile: " + file);
                return super.visitFile(file, attrs);
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                System.out.println("visitFileFailed: " + file);
                return super.visitFileFailed(file, exc);
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                System.out.println("postVisitDirectory: " + dir);
                return super.postVisitDirectory(dir, exc);
            }
        });
        // 递归删除目录及里面的文件
        Files.walkFileTree(Paths.get("C:\\AppData\\MyDir"), new SimpleFileVisitor<>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
        });
        // 只会遍历当前目录下的文件或目录，不会递归子目录的内容
        try (Stream<Path> pathStream = Files.list(Paths.get("C:\\AppData\\MyDir"))) {
            pathStream.forEach(System.out::println);
        }
        // =================================
        // 9. 查找
        // =================================
        try (Stream<Path> pathStream = Files.find(Paths.get("C:\\AppData\\MyDir"), Integer.MAX_VALUE, (p, a) -> p.toFile().getName().endsWith(".txt"))) {
            pathStream.forEach(System.out::println);
        }
    }

}